package mspbots.next.ticket.core.grader.interceptor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import mspbots.next.ticket.core.grader.AbstractGradeRuleAdapter;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.points.PointResult;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Jay.Yang
 */
@Service(value = "IN_PROCESS_STATUS_POINT_RULE")
public class InProcessStatusRuleInterceptor extends AbstractGradeRuleAdapter {

    private static final String RULE_KEY = "InProcessStatus";
    private List<InProcessStatusParam> params;
    private String identifier;

    @Override
    public Mono<GradeRuleInterceptorRegistration> prepare(GradeRuleInterceptorRegistration gradeRuleInterceptorRegistration) {
        return Flux.just()
                .doFinally(r -> {
                    this.params = super.objectMapper
                            .convertValue(gradeRuleInterceptorRegistration.getRule().getParams(), new TypeReference<>() {
                            });
                    this.identifier = gradeRuleInterceptorRegistration.getRule().getRequest().getIdentifier();
                })
                .then(Mono.just(gradeRuleInterceptorRegistration));
    }

    @Override
    public Mono<PointResult> scoring(JsonNode ticket) {
        AtomicInteger order = new AtomicInteger();
        Integer statusId = super.getIntValueId(ticket.get("status"));
        String statusName = super.getStringValueName(ticket.get("status"));
        String updatedBy = super.getStringValue(ticket.get("_info").get("updatedBy"));

        if (!StringUtils.hasLength(updatedBy) && !StringUtils.hasLength(this.identifier)) {
            params.stream().filter(item -> item.isSelected() && item.getStatusIds().contains(statusId))
                    .forEach(item -> {
                        if (this.identifier.equalsIgnoreCase(updatedBy) && !ObjectUtils.isEmpty(item.getActiveUserpoints())) {
                            order.addAndGet(item.getActiveUserpoints());
                        } else if (!ObjectUtils.isEmpty(item.getOtherUserPoints())) {
                            int points = item.getOtherUserPoints();
                            if (points > 0) {
                                points = -1 * points;
                            }
                            order.addAndGet(points);
                        }
                    });
        }
        return Mono.just(PointResult.withDefault().point(order.get())
                .detail(PointResult.Detail.with(order.get(), Map.of("statusName", statusName, "updatedBy", updatedBy),
                        RULE_KEY)));
    }

    @Data
    private static class InProcessStatusParam {
        private Integer index;
        private Integer activeUserpoints;
        private Integer otherUserPoints;
        private List<Integer> statusIds;
        private boolean selected;
    }
}
